﻿// WMS Library - http://wmslib.codeplex.com/
// Copyright (c) Tom Gaskins 2004 - Erik Putrycz 2013
// Licensed under the Microsoft Public License (Ms-PL)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using WMSLib.Client;
using SharpLog;

namespace WMSLib.WMSTools
{
    public class WMSUtils
    {
        private static ILogger logger = LogManager.CreateLogger<DebugLogger>();
        public const int MAX_THREADS = 4;

        public async static Task<List<Tuple<DateTime, T>>> ReadAllImages<T>(CancellationToken ct, IObserver<int> progress, List<Tuple<DateTime, Uri>> uris, Func<Stream, Task<T>> createImg) where T : class
        {
            var b = new List<Tuple<DateTime, T>>();
            var tasks = new List<Task>();
            for (int i = 0; i < MAX_THREADS; i++)
            {
                ct.ThrowIfCancellationRequested();
                tasks.Add(Task.Factory.StartNew(async () => await LoadImages<T>(ct, progress, uris, createImg, b), ct).Unwrap());
            }
            ct.ThrowIfCancellationRequested();
            Task.WaitAll(tasks.ToArray(),ct);
            progress.OnCompleted();
            return b;
        }

        private static async Task LoadImages<T>(CancellationToken ct, IObserver<int> progress, List<Tuple<DateTime, Uri>> uris, Func<Stream, Task<T>> createImg, List<Tuple<DateTime, T>> b) where T : class
        {
            using (var cl = new HttpClient())
            {
               // cts.Token.Register(() => cl.CancelPendingRequests());
                while (uris.Count > 0)
                {
                    ct.ThrowIfCancellationRequested();
                    Tuple<DateTime, Uri> result;
                    lock (uris)
                    {
                        result = uris[0];
                        uris.RemoveAt(0);
                    }
                    await ReadImage<T>(progress, cl, createImg, b, result, ct);
                }
            }
        }

        public static async Task<Tuple<T, string>> ReadLegend<T>(Layer.WMSStyle style, Func<Stream, Task<T>> createImg, CancellationToken ct) where T : class
        {
            if (style != null && style.LegendUri != null)
            {
                using (var cl = new HttpClient())
                {
                    using (var s = await cl.GetStreamAsync(style.LegendUri.Uri.Uri))
                    {
                        //var b = await CreateImageFromStream<T>(createImg, s, style.LegendUri.Uri.Uri, ct);
                        var b = await createImg(s);
                        return Tuple.Create(b, style.Title);
                    }
                }
            }
            await createImg(null);
            return null;
        }

        public static async Task<Tuple<byte[], string>> ReadLegend2(Layer.WMSStyle style)
        {
            if (style != null && style.LegendUri != null)
            {
                using (var cl = new HttpClient())
                {

                    using (var s = await cl.GetStreamAsync(style.LegendUri.Uri.Uri))
                    {
                        using (var m = new MemoryStream())
                        {
                            await s.CopyToAsync(m);
                            return Tuple.Create(m.ToArray(), style.Title);
                        }
                        //var b = await CreateImageFromStream<T>(createImg, s, style.LegendUri.Uri.Uri, ct);
                    }
                }
            }
            return null;
        }

        public static async Task<Tuple<T, string>> ReadAttribution<T>(Layer layer, Func<Stream, Task<T>> createImg, CancellationToken ct) where T : class
        {
            if (layer.Attribution != null)
            {
                using (var cl = new HttpClient())
                {
                    using (var s = await cl.GetStreamAsync(layer.Attribution.LogoUri.Uri.Uri))
                    {
                        //var b = await CreateImageFromStream<T>(createImg, s, layer.Attribution.LogoUri.Uri.Uri, ct);
                        var b = await createImg(s);
                        return Tuple.Create(b, layer.Attribution.Title);
                    }
                }
            }
            return null;
        }

        private static async Task ReadImage<T>(IObserver<int> progress, HttpClient cl, Func<Stream, Task<T>> createImg, List<Tuple<DateTime, T>> b, Tuple<DateTime, Uri> url, CancellationToken ct) where T : class
        {
            try
            {

                var img = await ReadImage(cl, createImg, url, ct);
                lock (b)
                {
                    b.Add(img);
                }
                progress.OnNext(1);
                ct.ThrowIfCancellationRequested();
            }
            catch (Exception wex)
            {
                //add logging
                logger.Error("Error loading image at '" + url.Item2 + "'", wex.ToString());
            }
        }

        public static async Task<Tuple<DateTime, T>> ReadImage<T>(Func<Stream, Task<T>> createImg, Tuple<DateTime, Uri> url, CancellationToken ct) where T : class
        {
            using (var cl = new HttpClient())
            {
                //ct.Register(() => cl.CancelPendingRequests());
                return await ReadImage(cl, createImg, url, ct);
            }
        }

        private static async Task<Tuple<DateTime, T>> ReadImage<T>(HttpClient cl, Func<Stream, Task<T>> createImg, Tuple<DateTime, Uri> url, CancellationToken ct) where T : class
        {
            try
            {
                Debug.WriteLine("Reading image {0}", url.Item2);
                using (var s = await cl.GetStreamAsync(url.Item2))
                {
                    //var img = await CreateImageFromStream<T>(createImg, s, url.Item2.AbsoluteUri,ct);
                    var img = await createImg(s);
                    return Tuple.Create(url.Item1, img);

                }
                //Assert.AreEqual(w, img.Width);
                //Assert.AreEqual(h, img.Height);                
            }
            catch (Exception wex)
            {
                //add logging
                if (!ct.IsCancellationRequested)
                    logger.Error("Error loading image at '" + url.Item2 + "'", wex.ToString());
                return null;
            }
        }

        //private static async Task<T> CreateImageFromStream<T>(Func<Stream, Task<T>> createImg, Stream s, string uri, CancellationToken ct) where T:class
        //{
        //    using (var ms = new MemoryStream())
        //    {
        //        await s.CopyToAsync(ms,4096,ct);
        //        ms.Position = 0;
        //        ct.ThrowIfCancellationRequested();
        //        try
        //        {
        //            return await createImg(ms);
        //        }
        //        catch (Exception ex)
        //        {
        //            logger.Error("Error reading image from stream " + uri, ex);
        //            return null;
        //        }
        //    }
        //}

    }
}
